#pragma once

#include <string>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>

#include "Log.hpp"

#define gbacklog 1024

class InetAddr
{
private:
    void ToHost()
    {
        _port = ::ntohs(_addr.sin_port);
        char ipbuf[32];
        ::inet_ntop(AF_INET, &_addr.sin_addr, ipbuf, sizeof ipbuf);
        _peerip = ipbuf;
    }

    void Swap(InetAddr& addr)
    {
        std::swap(_port, addr._port);
        std::swap(_peerip, addr._peerip);
        std::swap(_addr, addr._addr);
    }
public:
    InetAddr() {}
    InetAddr(const struct sockaddr_in& addr)
     : _addr(addr)
    {
        ToHost();
    }
    InetAddr(const InetAddr& addr)
     : _addr(addr._addr)
     , _peerip(addr._peerip)
     , _port(addr._port)
    {
    }
    InetAddr(InetAddr&& addr)
     : _addr(addr._addr)
     , _peerip(std::move(addr._peerip))
     , _port(addr._port)
    {
    }

    InetAddr& operator=(const InetAddr& addr)
    {
        InetAddr tmp(addr);
        Swap(tmp);
        return *this;
    }
    InetAddr& operator=(InetAddr&& addr)
    {
        Swap(addr);
        return *this;
    }

    bool operator==(const InetAddr& addr)
    {
        if (this == &addr)  return true;
        return _peerip == addr._peerip && _port == addr._port;
    }

    uint16_t GetPort() const
    {
        return _port;
    }
    std::string GetIp() const
    {
        return std::move(_peerip); // 移动构造返回
    }
    const struct sockaddr_in& GetAddr() const
    {
        return _addr;
    }
    struct sockaddr_in& GetAddr()
    {
        return _addr;
    }
    std::string GetAddrStr() const
    {
        std::string str;
        str = _peerip + ":" + std::to_string(_port);
        return std::move(str); // 移动构造返回
    }

    ~InetAddr() {}
private:
    std::string _peerip;
    uint16_t _port;
    struct sockaddr_in _addr;
};

class Socket
{
public:
    virtual bool CreatSocket() = 0;
    virtual bool Bind(uint16_t port) = 0;
    virtual bool Listen(int backlog = gbacklog) = 0;
    virtual int Accept(InetAddr* const clientaddr) = 0;
    virtual bool Connect(const std::string peerip, uint16_t port) = 0;
    virtual int GetSockfd() = 0;
    virtual void Close() = 0;
    virtual ssize_t Recv(std::string* const out, int flag = 0) = 0;
    virtual ssize_t Send(const std::string& in, int flag = 0) = 0;
    virtual void ReuseAddr() = 0; // 开启地址端口重用
    virtual void NonBlock() = 0; // 设置套接字属性为非阻塞
    virtual ssize_t NonBlockRecv(std::string* const out) = 0; // 设置为非阻塞接收
    virtual ssize_t NonBlockSend(const std::string& in) = 0; // 设置为非阻塞发送
    virtual ~Socket() = default;
public:
    bool BulidListenSocket(uint16_t port, int backlog = gbacklog, bool flag = 0)
    {
        if (!CreatSocket()) return false;
        if (flag) NonBlock();
        if (!Bind(port))    return false;
        if (!Listen(backlog))   return false;
        ReuseAddr();
        return true;
    }

    bool BulidClientSocket(const std::string peerip, uint16_t port)
    {
        if (!CreatSocket())     return false;
        if (!Connect(peerip, port)) return false;
        return true;
    }
};

class TcpSocket : public Socket
{
public:
    TcpSocket(int fd = -1)
     : _sockfd(fd)
    {
    }

    virtual bool CreatSocket() override
    {
        _sockfd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if (_sockfd < 0)
        {
            ERROR_LOG("socket creat failed!");
            return false;
        }
        INFO_LOG("socket creat success, sockfd: %d", _sockfd);
        return true;
    }

    virtual bool Bind(uint16_t port) override // 这个只需要提供给server，client是自动绑定的
    {
        struct sockaddr_in local;
        memset(&local, 0, sizeof local);
        local.sin_family = AF_INET; // 指定ip为IPV4
        local.sin_port = htons(port);
        local.sin_addr.s_addr = INADDR_ANY; // 服务器端进程任意IP地址绑定

        if (::bind(_sockfd, reinterpret_cast<struct sockaddr*>(&local), sizeof local) < 0)
        {
            ERROR_LOG("bind failed!");
            return false;
        }
        INFO_LOG("bind success!");
        return true;
    }

    virtual bool Listen(int backlog = gbacklog) override // 参数是限定同一时间的最大并发连接数，并不限制总数
    {
        if (::listen(_sockfd, backlog) < 0)
        {
            ERROR_LOG("listen failed!");
            return false;
        }
        INFO_LOG("listen success!");
        return true;
    }

    virtual int Accept(InetAddr* const clientaddr) override
    {
        struct sockaddr_in client;
        socklen_t len = sizeof client;

        int sockfd = ::(_sockfd, reinterpret_cast<struct sockaddr*>(&client), &len);
        if (sockfd < 0)
        {
            ERROR_LOG("accept failed!");
            return -1;
        }
        *clientaddr = InetAddr(client);
        INFO_LOG("accept success, sockfd: %d", sockfd);
        return sockfd;
    }

    virtual bool Connect(const std::string peerip, uint16_t port) override
    {
        struct sockaddr_in local;
        memset(&local, 0, sizeof local);
        local.sin_family = AF_INET; // 指定ip为IPV4
        local.sin_port = htons(port);
        local.sin_addr.s_addr = inet_addr(peerip.c_str());

        if (::connect(_sockfd, reinterpret_cast<struct sockaddr*>(&local), sizeof local) < 0)
        {
            ERROR_LOG("connect failed!");
            return false;
        }
        INFO_LOG("connect success!");
        return true;
    }

    virtual ssize_t Recv(std::string* const out, int flag = 0) override
    {
        ssize_t n = ::recv(_sockfd, &((*out)[0]), (*out).size(), flag);
        DEBUG_LOG("::recv size: %ld", n);
        if (n <= 0)
        {
            // EAGAIN表示当前socket缓冲区中没有数据，在非阻塞下才会有这个错误
            // EINTR表示当前socket阻塞等待，被信号打断了
            if (errno == EAGAIN || errno == EINTR)
                return 0; // 表示本次没有接收到数据
            ERROR_LOG("recv failed!");
            return -1;
        }
        return n; // 实际接受的数据长度
    }

    virtual ssize_t NonBlockRecv(std::string* const out) override // 设置套接字属性为非阻塞接收
    {
        return Recv(out, MSG_DONTROUTE);
    }

    virtual ssize_t Send(const std::string& in, int flag = 0) override
    {
        if (in.length() == 0)   return 0;
        ssize_t n = ::send(_sockfd, in.c_str(), in.length(), flag);
        DEBUG_LOG("::send size: %ld", n);
        if (n < 0)
        {
            if (errno == EAGAIN || errno == EINTR)
                return 0; // 表示本次没有发送出数据
            ERROR_LOG("send failed!");
            return -1;
        }
        return n; // 实际发送的数据长度
    }

    virtual ssize_t NonBlockSend(const std::string& in) override // 设置套接字属性为非阻塞发送
    {
        return Send(in, MSG_DONTROUTE);
    }

    virtual void ReuseAddr() override // 开启地址端口重用
    {
        int val = 1;
        setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<void*>(&val), sizeof val);
        val = 1;
        setsockopt(_sockfd, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast<void*>(&val), sizeof val);
    }

    virtual void NonBlock() override
    {
        int flag = fcntl(_sockfd, F_GETFL, 0);
        fcntl(_sockfd, F_SETFL, flag | O_NONBLOCK);
    }

    virtual int GetSockfd() override
    {
        return _sockfd;
    }

    virtual void Close() override
    {
        if (_sockfd != -1)
        {
            ::close(_sockfd);
            _sockfd = -1;
        }
    }

    virtual ~TcpSocket() override
    {
        Close();
    }
private:
    int _sockfd; // 可以是listensock，也可以是普通sockfd
};